package com.easyjoin.core;

import com.easyjoin.annotation.SelectColumn;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class EasyForeignListPropertyWriter extends BeanPropertyWriter {
    private final BeanPropertyWriter baseWriter;
    private final String foreignColumn;
    private final SelectColumn[] selectColumns;
    private final String tableName;
    private final boolean cache;
    private final String arrayAlias;
    private final DataSource dataSource;
    // [
    //   user_info:id:1 => {                     // 一级 key：外键定位一条实体
    //       "name" => [                         // 二级 map key = 字段名
    //           { "name": "仓库A", "location": "北京" },  // 该字段对应的一对多列表
    //           { "name": "仓库C", "location": "上海" }
    //       ],
    //       "location" => [
    //           { "name": "仓库A", "location": "北京" },
    //           { "name": "仓库C", "location": "上海" }
    //       ]
    //   }
    // ]
    private static final LoadingCache<String, ConcurrentHashMap<String, Optional<List<Map<String, String>>>>> TARGET_VALUE_CACHE =
            Caffeine.newBuilder()
                    .maximumSize(1000)
                    .expireAfterWrite(5, TimeUnit.SECONDS)
                    .build(key -> new ConcurrentHashMap<>());

    private static final Logger log = LoggerFactory.getLogger(EasyForeignListPropertyWriter.class);

    public EasyForeignListPropertyWriter(BeanPropertyWriter baseWriter, String foreignColumn, SelectColumn[] targetColumn, String tableName, boolean cache, String arrayAlias, DataSource dataSource) {
        super(baseWriter);
        this.baseWriter = baseWriter;
        this.foreignColumn = foreignColumn;
        this.selectColumns = targetColumn;
        this.tableName = tableName;
        this.dataSource = dataSource;
        this.cache = cache;
        this.arrayAlias = arrayAlias;
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
        long start = System.currentTimeMillis(); // 开始计时
        // 先序列化原字段
        baseWriter.serializeAsField(bean, gen, prov);
        // 取得加上注解的字段值（外建值）
        Object selfColumnValue = baseWriter.get(bean);
        if (selfColumnValue == null) return;
        // 取得缓存
        ConcurrentHashMap<String, Optional<List<Map<String, String>>>> cacheMap = getCacheMap(selfColumnValue);

        // 4️⃣ 任取一个字段的 value 作为列表（因为所有字段列表内容一样）
        List<Map<String, String>> cacheList = null;
        for (Optional<List<Map<String, String>>> opt : cacheMap.values()) {
            if (opt != null && opt.isPresent()) {
                cacheList = opt.get();
                break;
            }
        }
        if (cacheList == null || cacheList.isEmpty()) return;

        // 5️⃣ 输出数组字段
        gen.writeFieldName(arrayAlias);
        gen.writeStartArray();
        for (Map<String, String> row : cacheList) {
            gen.writeStartObject();
            for (SelectColumn col : selectColumns) {
                String columnName = col.alias().isEmpty() ? underlineToCamel(col.column()) : col.alias();
                String value = row.get(col.column());
                if (value == null) gen.writeNullField(columnName);
                else gen.writeStringField(columnName, value);
            }
            gen.writeEndObject();
        }
        gen.writeEndArray();
        long end = System.currentTimeMillis(); // 结束计时
        log.info("[EasyJoin] 序列化耗时: " + (end - start) + " ms, 字段: " + baseWriter.getName());
    }


    public ConcurrentHashMap<String, Optional<List<Map<String, String>>>> getCacheMap(Object selfColumnValue) {

        // 1️⃣ 构造缓存 key
        String cacheKey = tableName + ":" + foreignColumn + ":" + selfColumnValue;

        // 2️⃣ 从缓存取，若不存在自动创建
        ConcurrentHashMap<String, Optional<List<Map<String, String>>>> cacheMap =
                TARGET_VALUE_CACHE.get(cacheKey);

        // 3️⃣ 找出缺失字段
        List<String> missing = new ArrayList<>();
        for (SelectColumn col : selectColumns) {
            if (!cacheMap.containsKey(col.column())) {
                missing.add(col.column());
            }
        }

        // 4️⃣ 如果有缺失字段，需要查询所有字段 刷新缓存中所有value，因为取的逻辑是任取一条value，之所以这样
        // 是因为为了方便缺少字段合并缓存时提高效率，缓存的层级一级是表名字段值 二级是查询的字段名 二级的value才是完整的数据
        if (!missing.isEmpty()) {
            List<String> allSelectColumns = Arrays.stream(selectColumns).map(SelectColumn::column).collect(Collectors.toList());
            allSelectColumns.addAll(cacheMap.keySet());
            List<Map<String, String>> fetchedData = getTargetColumnValueList(
                    selfColumnValue,
                    foreignColumn,
                    allSelectColumns.toArray(new String[0]),
                    tableName,
                    dataSource
            );

            // 5️⃣ 整体刷新缓存
            if (!fetchedData.isEmpty()) {
                for (String columnName : allSelectColumns) {
                    // 用最新数据覆盖旧缓存
                    cacheMap.put(columnName, Optional.ofNullable(fetchedData.isEmpty() ? null : fetchedData));
                }
            }
        }


        return cacheMap;
    }



    @SneakyThrows
    public static List<Map<String, String>> getTargetColumnValueList(
            Object selfColumnValue,
            String foreignColumn,
            String[] targetColumn,
            String tableName,
            DataSource dataSource) {

        List<Map<String, String>> resultList = new ArrayList<>();
        String targetColumns = String.join(",", targetColumn);
        String querySql = "SELECT " + targetColumns + " FROM " + tableName + " WHERE " + foreignColumn + " = ?";

        try (Connection connection = dataSource.getConnection();
             PreparedStatement stmt = connection.prepareStatement(querySql)) {

            stmt.setObject(1, selfColumnValue);
            try (ResultSet rs = stmt.executeQuery()) {
                ResultSetMetaData metaData = rs.getMetaData();
                int columnCount = metaData.getColumnCount();

                while (rs.next()) {
                    Map<String, String> rowMap = new HashMap<>();
                    for (int i = 1; i <= columnCount; i++) {
                        String columnName = metaData.getColumnName(i);
                        Object columnValue = rs.getObject(i);
                        // 只存需要的字段
                        if (Arrays.asList(targetColumn).contains(columnName)) {
                            rowMap.put(columnName, columnValue == null ? null : columnValue.toString());
                        }
                    }
                    resultList.add(rowMap);
                }
            }
        } catch (SQLException e) {
            log.error("[EasyForeign] 查询外键映射异常，表: {}, 字段: {}, 值: {}, 错误: {}",
                    tableName, foreignColumn, selfColumnValue, e.getMessage(), e);
        }

        return resultList;
    }


    /**
     * 下划线转驼峰
     */
    private static String underlineToCamel(String underline) {
        if (underline == null || underline.isEmpty()) {
            return underline;
        }
        StringBuilder result = new StringBuilder();
        boolean upperCase = false;
        for (int i = 0; i < underline.length(); i++) {
            char c = underline.charAt(i);
            if (c == '_') {
                upperCase = true;
            } else if (upperCase) {
                result.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                result.append(Character.toLowerCase(c));
            }
        }
        return result.toString();
    }
}
